/*
 * Created on 2005/5/18
 */
package servidor;

/**
 * @author jefify
 */

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

import servidor.localizacao.ILocalizacao;
import servidor.localizacao.LocalizacaoSimuladaImpl;
import servidor.localizacao.LocalizacaoWAI;
import servidor.roteamento.Caminho;
import servidor.roteamento.Grafo;
import servidor.roteamento.Principal;
import servidor.roteamento.Vertice;

import comum.Localizacao;

public class ServidorImpl implements Servidor {

	private static HashMap<String, Localizacao[]> rotas = new HashMap<String, Localizacao[]>();
	
	public static Localizacao[] getRotas(String id) {
		return rotas.get(id);
	}

	public static void setRotas(HashMap<String, Localizacao[]> rotas) {
		ServidorImpl.rotas = rotas;
	}

	private static Principal principal;
	
	private static boolean flagSimulacao = false;
	
	// TODO remover
	private ILocalizacao localizacao;

	public ServidorImpl() {
		super();
		if (principal == null) {
		
			// para funcionar com bd descomente isto
			//try{
			//	principal = new Principal();
			//}
			//catch (SQLException e){
			//	System.out.println("Excessão: " + e.getMessage());
			//}
			
			//para funcionar com bd comente isto
			principal = new Principal();
			
			if (flagSimulacao) {
				System.out.println("WAI - WBLS4WAI - modo SIMULACAO");
				// TODO remover
			    localizacao = new LocalizacaoSimuladaImpl();
			} else {
			    System.out.println("WAI - WBLS4WAI Listener conectando... Aguarde...");
				// TODO remover
			    localizacao = new LocalizacaoWAI();			    
				System.out.println("WAI - WBLS4WAI Listener conectado com sucesso");
			}
		}
		System.out.println("Instanciou um servidor");
	}
	
	public Localizacao[] calculaRota(Localizacao ini, Localizacao fim, int restricao, String hdSerial) {
		//TODO Este método deverá tratar o retorno da direçao a ser seguida Localizacao.direction
		System.out.println("[ServidorImpl] Calculando rota...");
		Localizacao[] rotaMemoria = rotas.get(hdSerial);
		System.out.println(rotaMemoria == null ? "[ServidorImpl] Nao há rota na memória" : 
			"[ServidorImpl] Rota recuperada da memória");
		//Localizacao[] novaRota = this.retornaRota(ini, fim, restricao, hdSerial, rotaMemoria);
		Localizacao[] novaRota = this.retornaRota(ini, fim, restricao, hdSerial, null);
		rotas.put(hdSerial, novaRota);
		System.out.println("[ServidorImpl] Rota calculada...");
		return novaRota;
	}
	
	private Localizacao[] retornaRota(Localizacao ini, Localizacao fim, int restricao, String hdSerial, Localizacao[] rota) {
		 double fatorProximidade = 0.75;
		 double distanciaMedia = 78.05767098754612;
		 double raio = fatorProximidade * distanciaMedia;
		 		 
         if (rota == null || rota.length == 0) {
     		System.out.println("[ServidorImpl] Gerando rota...");
			 return this.geraRota(ini, fim, restricao);
         } else {
    		 int xDestino = rota[rota.length-1].getX();
    		 int yDestino = rota[rota.length-1].getY();
    		 int andarDestino = rota[rota.length-1].getAndar();
        	 
    		 if (xDestino != fim.getX() || yDestino != fim.getY() || andarDestino != fim.getAndar()) {
    	     	 System.out.println("[ServidorImpl] Gerando rota...");
    			 return this.geraRota(ini, fim, restricao);
    		 } else {
    			 if (rota.length == 1) {
    		     	 System.out.println("[ServidorImpl] Usuario está no destino final...");
    				 return rota;
    			 } else {
    				 int indiceMaisProximo = -1;
    				 double distanciaMinima = -1;
    				 for(int i=0; i < rota.length; i++) {
    					 if (rota[i].distancia(ini) < raio) {
    						 if (distanciaMinima == -1 || rota[i].distancia(ini) < distanciaMinima) {
    							 indiceMaisProximo = i;
    							 distanciaMinima = rota[i].distancia(ini);
    						 }
    					 }
    				 }
    				 if (indiceMaisProximo == -1 || indiceMaisProximo > 2) {
    			     	 System.out.println("[ServidorImpl] Gerando rota...");
    	    			 return this.geraRota(ini, fim, restricao);
    				 } else if (indiceMaisProximo == 0){
        		     	 System.out.println("[ServidorImpl] Usuario não saiu do lugar...");
    					 return rota;
    				 } else {
    					 Localizacao[] novaRota = new Localizacao[rota.length - indiceMaisProximo];
    					 for(int i=0; indiceMaisProximo < rota.length; i++, indiceMaisProximo++) {
    						 novaRota[i] = rota[indiceMaisProximo];
    					 }
        		     	 System.out.println("[ServidorImpl] Usuario está na rota previamente disponivel...");
    					 return novaRota;
    				 }
    			 }
    		 }
         }
	}

	public Localizacao[] geraRota(Localizacao ini, Localizacao fim, int restricao) {

		System.out.println("servidor.ServidorImpl.geraRota: ini = ( " + ini.getX() + ", " + ini.getY() + ", " + ini.getAndar() + " ); fim = ( " + fim.getX() + ", " + fim.getY() + ", " + fim.getAndar() + " )");

		double distanciaMinima = Double.MAX_VALUE;
		Vertice verticeInicial = null;
		Vertice verticeFinal  = null;
		Collection grafos = principal.getGrafoHierarquico();
		for (Iterator iter = grafos.iterator(); iter.hasNext();) {
			Grafo grafo = (Grafo) iter.next();
			//if (grafo.getNivelHierarquico() == 2) {
			if (grafo.getNivelHierarquico() >= 1) {
				Collection vertices = grafo.getVertices();
				for (Iterator iterator = vertices.iterator(); iterator.hasNext();) {
					Vertice vertice = (Vertice) iterator.next();
					if (vertice.getLocalizacao().getAndar() == ini.getAndar()) {
						double distancia = vertice.getLocalizacao().distancia(ini);
						if (distancia < distanciaMinima) {
							verticeInicial = vertice;
							distanciaMinima = distancia;
						}
					}
					if (vertice.getLocalizacao().getAndar() == fim.getAndar()) {
						double distancia = vertice.getLocalizacao().distancia(fim);
						if (distancia == 0) {
							verticeFinal = vertice;
						}
					}
				}
			}
		}
		
		if (verticeInicial == null || verticeFinal == null) {
			//TODO: ERRO!!!
			System.out.println("ERRO: null???  inicial: " + verticeInicial + ", final: " + verticeFinal);
		}
		
		Grafo grafo = null;
		Caminho caminhoDesejado = principal.buscarCaminhoHierarquico(verticeInicial, verticeFinal, restricao);
		System.out.println("CAMINHO: " + caminhoDesejado);
		System.out.println("CUSTO: " + caminhoDesejado.getCusto());

		ArrayList verticesCaminho = caminhoDesejado.getVertices();
		// devolve tambem a localizacao original no primeiro ponto
		// para facilitar a avaliacao no lado do cliente.
		Localizacao[] array = new Localizacao[verticesCaminho.size()+1];
		array[0] = ini;
		for (int i = 0; i < verticesCaminho.size(); i++) {
			Vertice v = (Vertice) verticesCaminho.get(i);
			array[i+1] = v.getLocalizacao();
		}

		return array;
	}

	// TODO remover esta funcao
	public Localizacao obtemLocalizacao() {
		Localizacao local = localizacao.obterLocalizacao();
		System.out.println("[Servidor] cliente: " + local);
		return local;
	}
		
	public Localizacao pegaLocalizacaoPeloNome(String strLocal) {
    	for (Iterator iter = principal.getGrafoHierarquico().iterator(); iter.hasNext();) {
    		Grafo grafo = (Grafo) iter.next();
            Vertice vertice = grafo.getVertice(strLocal);
            if (vertice != null) {
            	return vertice.getLocalizacao();
            }
        }
		return null;
	}
	
	public static void main(String[] args) {
		ServidorImpl.rotas = new HashMap<String, Localizacao[]>();
	}
}
